package parallels.servertech.server;

import java.io.*;
import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Receiver implements Runnable {
	private static final Logger log = Logger.getLogger("receiver");
	
	private static final int ACCEPT_TIMEOUT = 1000;
	private static final int AWAIT_SHUTDOWN_TIMEOUT = 5000;
	
	private final int port;
	private final Thread receiverThread;
	private final ExecutorService executor;
	
	private volatile boolean stopped = false;
	
	public Receiver(int port) {
		this.port = port;
		receiverThread = new Thread(this);
		executor = Executors.newFixedThreadPool(4);
	}
	
	public void start() {
		log.info("starting receiver thread");
		receiverThread.start();
		log.info("receiver thread started");
	}
	
	public void run() {
		try {
			ServerSocket serverSocket = new ServerSocket();
			
			log.info("trying to bind on port: " + port);
			serverSocket.bind(new InetSocketAddress(port));
			log.info("bound to port: " + port);
			
			serverSocket.setSoTimeout(ACCEPT_TIMEOUT);
			
			while (!stopped) {
				log.fine("waiting for client connection");
				Socket socket = null;
				try {
					socket = serverSocket.accept();
				} catch (SocketTimeoutException timeoutEx) {
					log.fine("no client connection received");
				}
				
				if (socket == null) {
					continue;
				}
				
				log.info("handling connection from: " + socket.getInetAddress().getHostAddress());
				ConnectionHandler handler = new ConnectionHandler(this, socket);
				
				executor.execute(handler);
			}
			
			serverSocket.close();
			log.info("server socket closed");
			
			log.info("stopping executor");
			executor.shutdownNow();
			
			try {
				boolean shutdown = executor.awaitTermination(AWAIT_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS);
				log.info("executor shut down: " + shutdown);
			} catch (InterruptedException intrEx) {
				log.info("interrupted");
			}
			
		} catch (IOException ioe) {
			log.log(Level.SEVERE, "error: ", ioe);
		}
	}
	
	public void stop() {
		log.info("stopping receiver");
		stopped = true;
	}
}